//////////////////////////////////////////////
// TreeReader.h
//
//////////////////////////////////////////////

/// Defines / Macros -------------------------

#pragma once

/// Forward decl -----------------------------

namespace nkExport
{
	class Node ;
}

/// Includes ---------------------------------

// nkExport
#include "../../Dll/DllDefines.h"

#include "../Node.h"

// nkMemory
#include <NilkinsMemory/Containers/BufferCast.h>
#include <NilkinsMemory/Containers/String.h>
#include <NilkinsMemory/Containers/StringView.h>

#include <NilkinsMemory/Pointers/UniquePtr.h>

// Standards
#include <functional>
#include <string>
#include <vector>

/// Internals --------------------------------

namespace nkExport
{
	class DLL_EXPORT_EXPORT TreeReaderEntry
	{
		public :

			// Constructor, destructor
			TreeReaderEntry (nkMemory::StringView path) noexcept ;
			TreeReaderEntry (const TreeReaderEntry&) noexcept ;
			TreeReaderEntry (TreeReaderEntry&&) noexcept ;
			virtual ~TreeReaderEntry () ;

			// Getters
			nkMemory::StringView getPath () const ;

			// Operators
			TreeReaderEntry& operator= (const TreeReaderEntry&) noexcept ;
			TreeReaderEntry& operator= (TreeReaderEntry&&) noexcept ;

			// Implementation
			virtual void fill (nkExport::Node*) = 0 ;

		protected :

			nkMemory::String _path ;
	} ;

	template <typename T>
	class TreeReaderTemplateEntry final : public TreeReaderEntry
	{
		public :

			// Constructor, destructor
			TreeReaderTemplateEntry (T& target, nkMemory::StringView path)
			:	TreeReaderEntry (path),
				_target (target)
			{
				// Nothing to do
			}

			virtual ~TreeReaderTemplateEntry () = default ;

			// Implementation
			virtual void fill (nkExport::Node*) override ;

		private :

			T& _target ;
	} ;

	template <>
	inline void TreeReaderTemplateEntry<std::string>::fill (nkExport::Node* node)
	{
		if (node)
			_target = node->getValueAsString() ;
	}

	template <template<typename...> typename C, typename T>
	class TreeReaderCompositeEntry : public TreeReaderEntry
	{
		public :

			// Constructor, destructor
			TreeReaderCompositeEntry (C<T>& target, std::function<T (nkExport::Node*)> entryFiller, nkMemory::StringView path)
			:	TreeReaderEntry (path),
				_target (target),
				_entryFiller (entryFiller)
			{
				// Nothing to do
			}

			virtual ~TreeReaderCompositeEntry () = default ;

			// Implementation
			virtual inline void fill (nkExport::Node* node) override
			{
				if (!node)
					return ;

				unsigned int childCount = node->getArraySize() ;

				for (unsigned int i = 0 ; i < childCount ; ++i)
				{
					if constexpr (std::is_same_v<C<T>, nkMemory::BufferCast<T>>)
						_target.append(_entryFiller(node->getArrayElement(i))) ;
					else if constexpr (std::is_same_v<C<T>, std::vector<T>>)
						_target.emplace_back(_entryFiller(node->getArrayElement(i))) ;
				}
			}

		protected :

			C<T>& _target ;
			std::function<T (nkExport::Node*)> _entryFiller ;
	} ;
}

/// Class ------------------------------------

namespace nkExport
{
	class DLL_EXPORT_EXPORT TreeReader final
	{
		public :

			// Constructor, destructor
			TreeReader () noexcept ;
			TreeReader (const TreeReader&) = delete ;
			TreeReader (TreeReader&& other) noexcept ;

			// Controls
			void addBool (bool& target, nkMemory::StringView path) ;
			void addInt (int& target, nkMemory::StringView path) ;
			void addUint (unsigned int& target, nkMemory::StringView path) ;
			void addFloat (float& target, nkMemory::StringView path) ;
			void addString (nkMemory::String& target, nkMemory::StringView path) ;

			template <typename T>
			void addVector (nkMemory::BufferCast<T>& target, std::function<T (nkExport::Node*)> entryFiller, nkMemory::StringView path)
			{
				_entries.append(new TreeReaderCompositeEntry<nkMemory::BufferCast, T>(target, entryFiller, path)) ;
			}

			template <typename T>
			void addVector (std::vector<T>& target, std::function<T (nkExport::Node*)> entryFiller, nkMemory::StringView path)
			{
				_entries.append(new TreeReaderCompositeEntry<std::vector, T>(target, entryFiller, path)) ;
			}

			template <typename T = std::string>
			void addString (std::string& target, nkMemory::StringView path)
			{
				_entries.append(new TreeReaderTemplateEntry<std::string> (target, path)) ;
			}

			// Filling
			void fill (nkExport::Node* tree) ;

			// Operators
			TreeReader& operator= (const TreeReader&) = delete ;
			TreeReader& operator= (TreeReader&&) noexcept ;

		private :

			// Attributes
			nkMemory::BufferCast<nkMemory::UniquePtr<TreeReaderEntry>> _entries ;
	} ;
}